/*
 * lir.c
 *
 * Implements a lower-level intermediate representation
 */

#include "lir.h"
#include "symtab.h"
#include "mem.h"

struct ir_t * currentBlock;
char * currentClass;
char currentFunc [128];
struct lir_t *lbbl_new(struct ir_t *);
struct operand_t * new_operand();
char * clName;
char * funcName;
extern char progName[256];

int lblockID = 2; // Counter for basic block IDs
//#define DEBUG
struct lir_t * lir_map_ir(struct ir_t *block) 
{
	ir_print_block(block);
	struct tac_t *stmt;
	struct lir_t *lblock = lbbl_new(block);
	currentClass = block->className;
	currentBlock = block;
	char label[256] = {};
	strcpy(label, block->id);
	strcat(label, ":");
	char * xx = malloc(strlen(label));
	strcpy(xx, label);
	instruction_new(&(lblock->instruction), MIPS_LABEL, NULL, NULL, NULL, NULL, xx);
	for(stmt = block->statements; stmt != NULL; stmt = stmt->next)
	{
		tac_to_mc(stmt, lblock);	// translate three address code to MIPS code
		if(stmt->next == NULL && block->cond != NULL && stmt->type == TAC_SET)  
		{
			char *temp_rd;
			char Blabel[256] = {};
			struct operand_t * help_temp_rd = new_operand();
			prepare_operand(stmt->v1, &help_temp_rd, 0, 0, currentClass, NULL);
			strcpy(Blabel, block->child->id);
			char * Bxx = malloc(strlen(Blabel));
			strcpy(Bxx, Blabel);
			instruction_new(&(lblock->instruction), MIPS_BEQ,
					help_temp_rd->lexeme, "$zero",
					NULL, NULL, Bxx);
			push_tmp_variable(help_temp_rd);
		}
	}
	if(block->next != block->child && block->cond == NULL)
	{
		char Jlabel[256] = {};
		strcpy(Jlabel, block->child->id);
		char * Jxx = malloc(strlen(Jlabel));
		strcpy(Jxx, Jlabel);
		instruction_new(&(lblock->instruction), MIPS_JUMP, NULL, NULL, NULL, NULL, Jxx);
	}
	return lblock;
}

/* ----------------------------------------------------------------------- 
 * Constructs a lir basic block
 * ----------------------------------------------------------------------- 
 */
struct lir_t *lbbl_new(struct ir_t *block) {
	struct lir_t *lbbl = malloc(sizeof(struct lir_t)), *temp, *parent;
	lbbl->lid = block->id;
	// Empty the lists
	lbbl->instruction = NULL;
	lbbl->numbered = 0;
	lbbl->stack = NULL;
	// add to global lir list
	add_to_global_lir_list(lbbl);	
	return lbbl;
}

void add_to_global_lir_list(struct lir_t *lir) {
	struct lir_list_t *temp = global_lir_list;
	if (temp->lir == NULL) {
		global_lir_list->lir = lir;
	} else {
		struct lir_list_t *newList = malloc(sizeof(struct lir_list_t));
		while (temp->next != NULL) {
			temp = temp->next;
		}
		temp->next = newList;
		newList->lir = lir;
	}	
}

struct lir_list_t *global_lir_list_init() {
	struct lir_list_t *ret = malloc(sizeof(struct lir_list_t));
	return ret;
}

/* returns 0 for Mem Type, 1 for register type */
void prepare_operand (char * var, struct operand_t ** result, int force, 
		int valType, char * class, char * func)
{
	struct operand_t * res = *result;
	struct symbol_t * sym;
	char * temp = NULL;

	if (force == 1)
	{
		strcpy(res->lexeme, var);
		return;
	}

	/* printf("SEARCHING FOR %s \n", var);
	   symtab_print(0); */
	sym = symtab_lookup (var, class, func, 
			SYMTAB_VARIABLE);
	if (!sym) {
		sym = symtab_lookup (var, class, NULL, SYMTAB_VARIABLE);
	}
	if (!sym)
	{
		/* it is a temp variable 
		 * Store it in a temp register */
		struct temp_variables_t * tmpReg = pop_tmp_variable (var, valType);

		if (tmpReg->contains_address_flag == 1)
			res->type = TYPE_TEMP_REGISTER_MEMORY;
		else
			res->type = TYPE_TEMP_REGISTER;

		strcpy(res->lexeme, tmpReg->name);
		res->offset = NOT_SET;
	}
	else
	{
		/* check if it is a func var or class var */
		if (sym->func)
		{
			/* if func variable */            
			/* sprintf(res->lexeme, "%d(%s)", sym->offset, MIPS_FP);*/
			strcpy (res->lexeme, MIPS_FP);
			res->offset = sym->offset;
			res->func = sym->func;
			res->type = TYPE_FUNCTION_MEMORY;
		}
		else
		{
			/* if class variable */
			strcpy (res->lexeme, MIPS_GLOBAL_PARENT_S0);
			res->offset = sym->offset;
			res->type = TYPE_CLASS_MEMORY;
			res->type_name = sym->data.type_name;
			res->var_name = var;
		}
	}

}

struct operand_t * new_operand()
{
	return malloc(sizeof(struct operand_t));
}

/* type 0: branch; type 1: nothing;	type 2: jump */
void tac_to_mc(struct tac_t *stmt, struct lir_t *lblock, int type) 
{
	int  temp_imm;
	char *temp_label;
	char *help_temp;
	char *negative_one;
	struct symbol_t * sym;

	struct operand_t * temp_rs = new_operand();
	struct operand_t * temp_rt = new_operand();
	struct operand_t * temp_rd = new_operand();

	struct operand_t * temp_rs2 = new_operand();
	struct operand_t * temp_rt2 = new_operand();
	struct operand_t * temp_rd2 = new_operand();

	struct operand_t * temp_rs3 = new_operand();
	struct operand_t * temp_rt3 = new_operand();
	struct operand_t * temp_rd3 = new_operand();

	struct operand_t * temp_rs4 = new_operand();
	struct operand_t * temp_rt4 = new_operand();
	struct operand_t * temp_rd4 = new_operand();

	struct operand_t * temp_rs5 = new_operand();
	struct operand_t * temp_rt5 = new_operand();
	struct operand_t * temp_rd5 = new_operand();

	struct operand_t * temp_rs6 = new_operand();
	struct operand_t * temp_rt6 = new_operand();
	struct operand_t * temp_rd6 = new_operand();

	struct operand_t * temp_rsSp = new_operand();
	struct operand_t * temp_rtSp = new_operand();
	struct operand_t * temp_rdSp = new_operand();

	// Convert true and false
	if (stmt->v2 != NULL && !strcasecmp(stmt->v2, "TRUE")) {
		stmt->v2 = NULL;
		stmt->n2 = 1;
	} else if (stmt->v2 != NULL && !strcasecmp(stmt->v2, "FALSE")) {
		stmt->v2 = NULL;
		stmt->n2 = 0;
	}

	if (stmt->v3 != NULL && !strcasecmp(stmt->v3, "TRUE")) {
		stmt->v3 = NULL;
		stmt->n2 = 1;
	} else if (stmt->v3 != NULL && !strcasecmp(stmt->v3, "FALSE")) {
		stmt->v3 = NULL;
		stmt->n2 = 0;
	}



#ifdef DEBUG
	ir_print_statement(stmt);
#endif    

	char labelName[512] = {};
	switch(stmt->type) {
		case TAC_ENTER:
			memset (currentFunc, 0, sizeof(currentFunc));
			strcpy (currentFunc, stmt->v2);
			/* Add function label */				
			strcpy (labelName, currentClass);                
			strcat (labelName, "_");
			strcat (labelName, currentFunc);
			strcat (labelName, ":");

			prepare_operand (labelName, &temp_rd, 1, 0, currentClass, NULL);
			instruction_new(&(lblock->instruction), MIPS_LABEL, temp_rd->lexeme,
					temp_rd->lexeme, temp_rd->lexeme, NULL, temp_rd->lexeme);

			/* store the parent class name to $s0 - lw $s0 PA(FP) */
			prepare_operand (MIPS_GLOBAL_PARENT_S0, &temp_rd2, 1, 0, currentClass, NULL);
			prepare_operand (MIPS_FP, &temp_rs2, 1, 0, currentClass, NULL);
			temp_imm = MIPS_FUNC_PA_CLASS_OFFSET;

			instruction_new(&(lblock->instruction), MIPS_LOAD_WORD, 
					temp_rs2->lexeme, temp_rt2->lexeme, 
					temp_rd2->lexeme, temp_imm, NULL);

			/* Store Global RA to local RA - sw $ra RA(FP)*/
			prepare_operand (MIPS_FP, &temp_rd3, 1, 0, currentClass, NULL);
			prepare_operand (MIPS_RA, &temp_rs3, 1, 0, currentClass, NULL);
			temp_imm = MIPS_FUNC_RA_OFFSET;

			instruction_new(&(lblock->instruction), MIPS_STORE_WORD, 
					temp_rs3->lexeme, temp_rt3->lexeme, 
					temp_rd3->lexeme, temp_imm, NULL);
			break;
		case TAC_RETURN:
			{
				if (strcmp(tolower(progName), tolower(stmt->v2)))           
				{
					/* SETTING THE FP FROM LOCAL PA(FP) */
					/* lw  $tX, PA($fp)*/
					prepare_operand (MIPS_FP, &temp_rs, 1, 0, currentClass, NULL);
					prepare_operand ("__funcret__", &temp_rd, 0, 0, currentClass, NULL);
					temp_imm = MIPS_FUNC_PA_FUNC_OFFSET;
					instruction_new(&(lblock->instruction), MIPS_LOAD_WORD, temp_rs->lexeme,
							temp_rt->lexeme, temp_rd->lexeme, temp_imm, NULL);

					/* lw $tX RA(FP) */
					prepare_operand (MIPS_FP, &temp_rs3, 1, 0, currentClass, NULL);
					prepare_operand ("__localRA__", &temp_rd3, 0, 0, currentClass, NULL);
					temp_imm = MIPS_FUNC_RA_OFFSET;
					instruction_new(&(lblock->instruction), MIPS_LOAD_WORD, temp_rs3->lexeme,
							temp_rt3->lexeme, temp_rd3->lexeme, temp_imm, NULL);

					/* Before changing the function pointer. Load the return
					 * value to $s1: lw $s1, off(fp) -- > if and only of return variable exsists */
					struct symbol_t * sym = symtab_lookup (stmt->v2, currentClass, stmt->v2, SYMTAB_VARIABLE);
					if (sym)
					{
						printf("YES THERE IS RETURN. LOADING TO S1 \n");
						/* ok return variable exists */
						/* loading the return add to s1 */
						instruction_new(&(lblock->instruction), MIPS_LOAD_WORD, MIPS_FP,
								NULL, MIPS_FUNC_RETURN_S1, sym->offset, NULL);
					}



					/* add $fp $tX, $zero*/
					prepare_operand ("__funcret__", &temp_rs2, 0, 0, currentClass, NULL);
					prepare_operand ("$zero", &temp_rt2, 1, 0, currentClass, NULL);
					prepare_operand (MIPS_FP, &temp_rd2, 1, 0, currentClass, NULL);
					temp_imm = NULL;
					instruction_new(&(lblock->instruction), MIPS_ADD, temp_rs2->lexeme,
							temp_rt2->lexeme, temp_rd2->lexeme, temp_imm, NULL);

					/* lw %s0 $fp */
					instruction_new(&(lblock->instruction), MIPS_LOAD_WORD, MIPS_FP,
							NULL, MIPS_GLOBAL_PARENT_S0, 0, NULL);

					/* JUMPING TO RA*/
					/* j $tX - tX is from above */
					instruction_new(&(lblock->instruction), MIPS_JUMP, temp_rd3->lexeme,
							temp_rd3->lexeme, temp_rd3->lexeme, temp_imm, temp_rd3->lexeme);

					push_tmp_variable(temp_rd->lexeme);
					push_tmp_variable(temp_rd3->lexeme);

				}
				else
				{
					instruction_new(&(lblock->instruction), MIPS_JUMP, NULL,
							NULL, NULL, NULL, OUT_YOU_GO);
				}
				break;
			}
		case TAC_PRINT:
			{
				prepare_operand(stmt->v2, &temp_rs, 0, 0, currentClass, NULL);
				if (temp_rs->type == TYPE_CLASS_MEMORY || temp_rs->type == TYPE_FUNCTION_MEMORY) {
					// We are printing the value of a variable. First load it into a register
					struct temp_variables_t *tempValue = pop_tmp_variable("__temp__", 0);
					instruction_new(&(lblock->instruction), MIPS_LOAD_WORD, temp_rs->lexeme, NULL, tempValue->name, temp_rs->offset, NULL);
					// Value is in the temporary registers, now let's print it out
					instruction_new(&(lblock->instruction), MIPS_ADD_I, tempValue->name, NULL, MIPS_PRINT_A0, 0, NULL);
					push_tmp_variable(tempValue->name);
				} else {
					if (temp_rs->type == TYPE_TEMP_REGISTER) {
						// The second operand is a value, so load that and print
						instruction_new(&(lblock->instruction), MIPS_ADD_I, temp_rs->lexeme, NULL, MIPS_PRINT_A0, 0, NULL);
					} else {
						// The second operand is a pointer to memory, so load the value from there and print it
						instruction_new(&(lblock->instruction), MIPS_LOAD_WORD, temp_rs->lexeme, NULL, MIPS_PRINT_A0, 0, NULL);
					}
				}
				// First print the value
				instruction_new(&(lblock->instruction), MIPS_LOAD_IMM, NULL, NULL, MIPS_V0, 1, NULL);
				instruction_new(&(lblock->instruction), MIPS_LABEL, NULL, NULL, NULL, NULL, MIPS_SYS_CALL);
				// Now print the newline
				instruction_new(&(lblock->instruction), MIPS_LOAD_ADD, MIPS_NEWLINE, NULL, MIPS_PRINT_A0, NULL, NULL);
				instruction_new(&(lblock->instruction), MIPS_LOAD_IMM, NULL, NULL, MIPS_V0, 4, NULL);
				instruction_new(&(lblock->instruction), MIPS_LABEL, NULL, NULL, NULL, NULL, MIPS_SYS_CALL);
				push_tmp_variable(temp_rs->lexeme);
				break;
			}
		case TAC_SET:
			// Get the second operand first
			if (stmt->v2) {
				prepare_operand(stmt->v2, &temp_rs, 0, 0, currentClass, NULL);
				if (temp_rs->type == TYPE_CLASS_MEMORY || temp_rs->type == TYPE_FUNCTION_MEMORY) {
					// The second operand is a variable, so first get the value at the offset of the variable
					struct temp_variables_t *tempPrevResult = pop_tmp_variable(stmt->v1, 0);
					if (tempPrevResult->contains_address_flag == 1) {
						// The first operand is a memory location. Store the value in the second operand in it
						struct temp_variables_t *tempValue = pop_tmp_variable("__temp__", 0);
						instruction_new(&(lblock->instruction), MIPS_LOAD_WORD, temp_rs->lexeme, NULL, tempValue->name, temp_rs->offset, NULL);
						// The value is in the register, so store it in memory
						instruction_new(&(lblock->instruction), MIPS_STORE_WORD, tempValue->name, NULL, tempPrevResult->name, 0, NULL);
						push_tmp_variable(tempValue->name);
						push_tmp_variable(tempPrevResult->name);
					} else {
						// The first operand hasn't been created yet. Let's store the value in it
						struct temp_variables_t *tempValue = pop_tmp_variable("__temp__", 0);
						instruction_new(&(lblock->instruction), MIPS_LOAD_WORD, temp_rs->lexeme, NULL, tempValue->name, temp_rs->offset, NULL);
						// The value is in the register, so store it in memory
						instruction_new(&(lblock->instruction), MIPS_ADD_I, tempValue->name, NULL, tempPrevResult->name, 0, NULL);
						push_tmp_variable(tempValue->name);
					}
				} else {
					// The second operand is a temporary variable of some sort. Find out what kind it is
					if (temp_rs->type == TYPE_TEMP_REGISTER) {
						// We are dealing with a value in the second operand. See what lies in the first operand
						prepare_operand(stmt->v1, &temp_rd, 0, 0, currentClass, NULL);
						if (temp_rd->type == TYPE_CLASS_MEMORY || temp_rd->type == TYPE_FUNCTION_MEMORY) {
							// The first operand is user-defined, so store the value into it
							instruction_new(&(lblock->instruction), MIPS_STORE_WORD, temp_rs->lexeme, NULL, temp_rd->lexeme, temp_rd->offset, NULL);
							push_tmp_variable(temp_rd->lexeme);
						} else {
							struct temp_variables_t *tempPrevResult = pop_tmp_variable(stmt->v1, 0);
							if (tempPrevResult->contains_address_flag == 1) {
								// The first operand is a memory location. Store the value in the second operand into it
								instruction_new(&(lblock->instruction), MIPS_STORE_WORD, temp_rs->lexeme, NULL, tempPrevResult->name, 0, NULL); 
								push_tmp_variable(tempPrevResult->name);
							} else {
								// The first operand hasn't been created yet. Let's store the value in it
								instruction_new(&(lblock->instruction), MIPS_ADD_I, temp_rs->lexeme, NULL, tempPrevResult->name, 0, NULL);  
							}
						}
					} else {
						// We are dealing with a memory location in the second operand. Copy it's value into the destination
						struct temp_variables_t *tempValue = pop_tmp_variable(stmt->v1, 0);
						instruction_new(&(lblock->instruction), MIPS_LOAD_WORD, temp_rs->lexeme, NULL, tempValue->name, 0, NULL);
					}
				}
				push_tmp_variable(temp_rs->lexeme);
			} else {
				// We are making the assumption that n2 will be set if v2 is not
				// We are dealing with an immediate value. The only situation this occurs in is loading into a new temporary register
				struct temp_variables_t *tempResult = pop_tmp_variable(stmt->v1, 0);
				instruction_new(&(lblock->instruction), MIPS_LOAD_IMM, NULL, NULL, tempResult->name, stmt->n2, NULL);
			}
			break;
		case TAC_FUNC:
			{
#ifdef DEBUG        
				instruction_new (&(lblock->instruction), MIPS_LABEL, NULL, NULL, NULL, NULL, "TAC_FUNC:");
#endif
				/* The statement is of the following format 
				 * eg: t2	function	testfunc2	testclass1 
				 *     stmt->v1 = t2 (This is the return value reg) 
				 *     stmt->v2 = testfunc2 (Function name to be called)
				 *     stmt->v3 = testclass1
				 *     (className/currentClass-ObjecName/tempReg-holding-classname)
				 * */

				/* We have 3 cases here. 
				 * 1) call func directly func()
				 * 2) call func from obj in current class obj.func()
				 * 3) calling func when there are more than 1 funcs obj.obj2.func()
				 * */
				int localFlag = -1;  
				struct symbol_t * sym_obj;
				struct temp_variables_t * tempRegForClass = pop_tmp_variable(stmt->v3, 1);
				struct temp_variables_t * tempReg = pop_tmp_variable("__final__", 1);

				/* check if it is a class name Case 1 */
				if (usrdef_lookup (stmt->v3))
				{
					/* loading S0 into temp register */
					instruction_new (&(lblock->instruction), MIPS_ADD_I, 
							MIPS_GLOBAL_PARENT_S0, NULL, tempReg->name, 
							0, NULL);
					clName = stmt->v3;
					funcName = stmt->v2;
					push_tmp_variable(tempRegForClass->name);
				}
				/* check if it is a variable of currentClass */
				else if (sym_obj = (symtab_lookup (stmt->v3, currentClass, NULL, SYMTAB_VARIABLE)))
				{
					/* In here tempRegForClass is a new temp register to store the
					 * address of the object as it is the parent of the calling 
					 * function addi $tX, $S0, offset */
					/* lw $tY off(S0) */

					instruction_new (&(lblock->instruction), MIPS_LOAD_WORD, 
							MIPS_GLOBAL_PARENT_S0, NULL, tempRegForClass->name, 
							(int) sym_obj->offset, NULL);

					instruction_new (&(lblock->instruction), MIPS_ADD_I, 
							tempRegForClass->name, NULL, tempReg->name, 
							0, NULL);
					clName = sym_obj->data.type_name;
					funcName = stmt->v2;
				}
				/* more than one objects */
				else
				{
					/* pop the temp reg associated with this statement and 
					 * get the class name from it */
					clName = tempRegForClass->type_name;

					instruction_new (&(lblock->instruction), MIPS_LOAD_WORD, 
							tempRegForClass->name, NULL, tempReg->name, 
							0, NULL);
				}

				/* load fp to some temp Variable XX  - addi $tX $zero $fp*/
				prepare_operand ("__curFp__", &temp_rd, 0, 0, currentClass, NULL);
				prepare_operand ("$zero", &temp_rs, 1, 0, currentClass, NULL);
				prepare_operand (MIPS_FP, &temp_rt, 1, 0, currentClass, NULL);

				instruction_new(&(lblock->instruction), MIPS_ADD, temp_rs->lexeme,
						temp_rt->lexeme, temp_rd->lexeme, temp_imm, NULL);

				/* set fp to be current sp - addi $fp $zero $sp */
				prepare_operand ("__curSP__", &temp_rd2, 0, 0, currentClass, NULL);
				prepare_operand ("$zero", &temp_rs2, 1, 0, currentClass, NULL);
				prepare_operand (MIPS_SP, &temp_rt2, 1, 0, currentClass, NULL);
				instruction_new(&(lblock->instruction), MIPS_ADD, temp_rs2->lexeme,
						temp_rt2->lexeme, temp_rd2->lexeme, temp_imm, NULL);

				instruction_new(&(lblock->instruction), MIPS_STORE_WORD, tempReg->name, NULL, MIPS_SP, 0, NULL);

				instruction_new(&(lblock->instruction), MIPS_STORE_WORD, temp_rd->lexeme, NULL, MIPS_SP, 8, NULL);

				struct temp_variables_t *nextPrevSP = pop_tmp_variable("__nextSP__", 0);
				instruction_new(&(lblock->instruction), MIPS_ADD, temp_rs2->lexeme, MIPS_SP, nextPrevSP->name, NULL, NULL);

				int size = symtab_get_func_size(stmt->v2);
				prepare_operand (MIPS_SP, &temp_rdSp, 1, 0, currentClass, NULL);
				prepare_operand ("__curSP__", &temp_rsSp, 0, 0, currentClass, NULL);
				temp_imm = size;

				instruction_new(&(lblock->instruction), MIPS_ADD_I, nextPrevSP->name,
						nextPrevSP->name, temp_rdSp->lexeme, temp_imm, NULL);


				push_tmp_variable (temp_rs6->lexeme);
				push_tmp_variable (temp_rd->lexeme);
				push_tmp_variable (tempReg->name);

				break;
			}
		case TAC_END_FUNC:
			{
#ifdef DEBUG        
				instruction_new (&(lblock->instruction), MIPS_LABEL, NULL, NULL, NULL, NULL, "TAC_END_FUNC:");
#endif            
				/* The statement is of the following format 
				 * eg: t2	end function	testfunc2	testclass1 
				 *     stmt->v1 = t2 (This is the return value reg) 
				 *     stmt->v2 = testfunc2 (Function name to be called)
				 *     stmt->v3 = testclass1
				 *     (className/currentClass-ObjecName/tempReg-holding-classname)
				 * */

				/* We have 3 cases here. 
				 * 1) call func directly func()
				 * 2) call func from obj in current class obj.func()
				 * 3) calling func when there are more than 1 funcs obj.obj2.func()
				 * */
				struct temp_variables_t *sp = pop_tmp_variable("__curSP__", 0);
				instruction_new(&(lblock->instruction), MIPS_ADD, sp->name, "$zero", MIPS_FP, NULL, NULL);
				push_tmp_variable("__curSP__");
				struct temp_variables_t * tempRegForFuncEnd = NULL;
                struct symbol_t * symObj;
				/* check if it is a class name Case 1 */
				if (usrdef_lookup (stmt->v3))
				{
					clName = stmt->v3;
					funcName = stmt->v2;
				}
				/* check if it is a variable of currentClass */
				else if (symObj = symtab_lookup (stmt->v3, currentClass, NULL, SYMTAB_VARIABLE))
				{
					tempRegForFuncEnd = (struct temp_variable_t *)pop_tmp_variable(stmt->v3, 1);
					clName = symObj->data.type_name;
					funcName = stmt->v2;
				}
				/* more than one objects */
				else
				{
					/* pop the temp reg associated with this statement and 
					 * get the class name from it */
					tempRegForFuncEnd = (struct temp_variable_t *)pop_tmp_variable(stmt->v3, 1);
					clName = tempRegForFuncEnd->type_name;
				}

				/* This is actual jump to the function: jla labelName*/
				strcpy (labelName, clName);                
				strcat (labelName, "_");
				strcat (labelName, funcName);
				prepare_operand (labelName, &temp_rd4, 1, 0, currentClass, NULL);

				instruction_new(&(lblock->instruction), MIPS_JUMP_LINK, temp_rs4->lexeme,
						temp_rt4->lexeme, temp_rd4->lexeme, temp_imm, temp_rd4->lexeme);


				/* loading the return value from s1 */
				/* but before that check, if the function being called has a return
				 * value */
				struct symbol_t *var = symtab_lookup (funcName, clName, funcName, SYMTAB_VARIABLE);
				if (var)
				{
					/* Yes there is a return value */
					/* now do lw $tX, $s1 */
					struct temp_variables_t * tempRet = pop_tmp_variable(stmt->v1, 0);
					instruction_new (&(lblock->instruction), MIPS_ADD_I, MIPS_FUNC_RETURN_S1, NULL, tempRet->name, 0, NULL);
					if (strcmp(var->data.type_name,"integer") != 0) tempRet->contains_address_flag = 1;
				}

				
				if (tempRegForFuncEnd)
					push_tmp_variable(tempRegForFuncEnd->name); 

				break;        
			}
		case TAC_EQUAL:
			prepare_operand(stmt->v2, &temp_rs, 0 ,0, currentClass, NULL);
			if(stmt->v2 != NULL)
			{
				prepare_operand(stmt->v3, &temp_rt, 0, 0, currentClass, NULL);
				instruction_new(&(lblock->instruction), MIPS_BNE,
						temp_rs->lexeme, temp_rt->lexeme,
						NULL, NULL, currentBlock->child->id);
				push_tmp_variable(temp_rs->lexeme);
				push_tmp_variable(temp_rt->lexeme);
			}
			else
			{
				temp_imm = stmt->n2;
				help_temp = pop_tmp_variable("help_temp", 0);
				instruction_new(&(lblock->instruction), MIPS_ADD_I,
						"$zero", NULL,
						help_temp, temp_imm, NULL);
				instruction_new(&(lblock->instruction), MIPS_BNE,
						temp_rs->lexeme, help_temp,
						NULL, NULL, currentBlock->child->id);
				push_tmp_variable(temp_rs->lexeme);
				push_tmp_variable(help_temp);
			}
			break;
		case TAC_NOTEQ:
			prepare_operand(stmt->v2, &temp_rs, 0 ,0, currentClass, NULL);
			if(stmt->v2 != NULL)
			{
				prepare_operand(stmt->v3, &temp_rt, 0, 0, currentClass, NULL);
				instruction_new(&(lblock->instruction), MIPS_BEQ,
						temp_rs->lexeme, temp_rt->lexeme,
						NULL, NULL, currentBlock->child->id);
				push_tmp_variable(temp_rs->lexeme);
				push_tmp_variable(temp_rt->lexeme);
			}
			else
			{
				temp_imm = stmt->n2;
				help_temp = pop_tmp_variable("help_temp", 0);
				instruction_new(&(lblock->instruction), MIPS_ADD_I,
						"$zero", NULL,
						help_temp, temp_imm, NULL);
				instruction_new(&(lblock->instruction), MIPS_BEQ,
						temp_rs->lexeme, help_temp,
						NULL, NULL, currentBlock->child->id);
				push_tmp_variable(temp_rs->lexeme);
				push_tmp_variable(help_temp);
			}
			break;
		case TAC_LT:
			prepare_operand(stmt->v1, &temp_rd, 0 ,0, currentClass,NULL);
			prepare_operand(stmt->v2, &temp_rs, 0 ,0, currentClass, NULL);
			if(stmt->v2 != NULL)
			{
				prepare_operand(stmt->v3, &temp_rt, 0, 0, currentClass, NULL);
				instruction_new(&(lblock->instruction), MIPS_SET_LESS_THAN,
						temp_rs->lexeme, temp_rt->lexeme,
						temp_rd->lexeme, NULL, NULL);
				push_tmp_variable(temp_rs->lexeme);
				push_tmp_variable(temp_rt->lexeme);
				instruction_new(&(lblock->instruction), MIPS_BEQ,
						temp_rd->lexeme, "$zero",
						NULL, NULL, currentBlock->child->id);
				push_tmp_variable(temp_rd->lexeme);		
			}
			else
			{
				temp_imm = stmt->n2;
				instruction_new(&(lblock->instruction), MIPS_SET_LESS_THAN,
						temp_rs->lexeme, NULL,
						temp_rd->lexeme, temp_imm, NULL);
				push_tmp_variable(temp_rs->lexeme);
				instruction_new(&(lblock->instruction), MIPS_BEQ,
						temp_rd->lexeme, "$zero",
						NULL, NULL, currentBlock->child->id);
				push_tmp_variable(temp_rd->lexeme);
			}
			break;
		case TAC_GT:
			prepare_operand(stmt->v1, &temp_rd, 0 ,0, currentClass, NULL);
			prepare_operand(stmt->v2, &temp_rs, 0 ,0, currentClass, NULL);
			if(stmt->v2 != NULL)
			{
				prepare_operand(stmt->v3, &temp_rt, 0, 0, currentClass, NULL);
				instruction_new(&(lblock->instruction), MIPS_BEQ,
						temp_rs->lexeme, temp_rt->lexeme,
						NULL, NULL, currentBlock->child->id);
				instruction_new(&(lblock->instruction), MIPS_SET_LESS_THAN,
						temp_rs->lexeme, temp_rt->lexeme,
						temp_rd->lexeme, NULL, NULL);
				push_tmp_variable(temp_rs->lexeme);
				push_tmp_variable(temp_rt->lexeme);
				instruction_new(&(lblock->instruction), MIPS_BNE,
						temp_rd->lexeme, "$zero",
						NULL, NULL, currentBlock->child->id);
				push_tmp_variable(temp_rd->lexeme);		
			}
			else
			{
				temp_imm = stmt->n2;
				help_temp = pop_tmp_variable("help_temp", 0);
				instruction_new(&(lblock->instruction), MIPS_ADD_I,
						"$zero", NULL,
						help_temp, temp_imm, NULL);
				instruction_new(&(lblock->instruction), MIPS_BEQ,
						temp_rs->lexeme, help_temp,
						NULL, NULL, currentBlock->child->id);	
				instruction_new(&(lblock->instruction), MIPS_SET_LESS_THAN,
						temp_rs->lexeme, help_temp,
						temp_rd->lexeme, temp_imm, NULL);
				push_tmp_variable(temp_rs->lexeme);
				push_tmp_variable(help_temp);
				instruction_new(&(lblock->instruction), MIPS_BNE,
						temp_rd->lexeme, "$zero",
						NULL, NULL, currentBlock->child->id);
				push_tmp_variable(temp_rd->lexeme);
			}
			break;
		case TAC_LEQ: 
			prepare_operand(stmt->v1, &temp_rd, 0 ,0, currentClass, NULL);
			prepare_operand(stmt->v2, &temp_rs, 0 ,0, currentClass, NULL);
			if(stmt->v2 != NULL)
			{
				prepare_operand(stmt->v3, &temp_rt, 0, 0, currentClass, NULL);
				instruction_new(&(lblock->instruction), MIPS_SUBTRACT,
						temp_rt->lexeme, temp_rs->lexeme,
						temp_rd->lexeme, NULL, NULL);
				instruction_new(&(lblock->instruction), MIPS_SET_LESS_THAN,
						temp_rd->lexeme, "$zero",
						temp_rd->lexeme, NULL, NULL);
				push_tmp_variable(temp_rs->lexeme);
				push_tmp_variable(temp_rt->lexeme);
				instruction_new(&(lblock->instruction), MIPS_BNE,
						temp_rd->lexeme, "$zero",
						NULL, NULL, currentBlock->child->id);
				push_tmp_variable(temp_rd->lexeme);		
			}
			else
			{
				temp_imm = stmt->n2;
				help_temp = pop_tmp_variable("help_temp", 0);
				instruction_new(&(lblock->instruction), MIPS_ADD_I,
						"$zero", NULL,
						help_temp, temp_imm, NULL);
				instruction_new(&(lblock->instruction), MIPS_SUBTRACT,
						help_temp, temp_rs->lexeme,
						temp_rd->lexeme, NULL, currentBlock->child->id);
				instruction_new(&(lblock->instruction), MIPS_SET_LESS_THAN,
						temp_rd->lexeme, "$zero",
						temp_rd->lexeme, temp_imm, NULL);
				push_tmp_variable(temp_rs->lexeme);
				push_tmp_variable(help_temp);
				instruction_new(&(lblock->instruction), MIPS_BNE,
						temp_rd->lexeme, "$zero",
						NULL, NULL, currentBlock->child->id);
				push_tmp_variable(temp_rd->lexeme);
			}
			break;
		case TAC_GEQ: 
			prepare_operand(stmt->v1, &temp_rd, 0 ,0, currentClass, NULL);
			prepare_operand(stmt->v2, &temp_rs, 0 ,0, currentClass, NULL);
			if(stmt->v2 != NULL)
			{
				prepare_operand(stmt->v3, &temp_rt, 0, 0, currentClass, NULL);
				instruction_new(&(lblock->instruction), MIPS_SET_LESS_THAN,
						temp_rs->lexeme, temp_rt->lexeme,
						temp_rd->lexeme, NULL, NULL);
				push_tmp_variable(temp_rs->lexeme);
				push_tmp_variable(temp_rt->lexeme);
				instruction_new(&(lblock->instruction), MIPS_BNE,
						temp_rd->lexeme, "$zero",
						NULL, NULL, currentBlock->child->id);
				push_tmp_variable(temp_rd->lexeme);		
			}
			else
			{
				temp_imm = stmt->n2;
				instruction_new(&(lblock->instruction), MIPS_SET_LESS_THAN,
						temp_rs->lexeme, NULL,
						temp_rd->lexeme, temp_imm, NULL);
				push_tmp_variable(temp_rs->lexeme);
				instruction_new(&(lblock->instruction), MIPS_BNE,
						temp_rd->lexeme, "$zero",
						NULL, NULL, currentBlock->child->id);
				push_tmp_variable(temp_rd->lexeme);
			}
			break;
		case TAC_PLUS:
			prepare_operand(stmt->v2, &temp_rs, 0, 0, currentClass, NULL);
			prepare_operand(stmt->v1, &temp_rd, 0, 0, currentClass, NULL);
			if(stmt->v2 != NULL)
			{
				prepare_operand(stmt->v3, &temp_rt, 0, 0, currentClass, NULL);
				instruction_new(&(lblock->instruction), MIPS_ADD,
						temp_rs->lexeme, temp_rt->lexeme,
						temp_rd->lexeme, NULL, NULL);
				push_tmp_variable(temp_rs->lexeme);
				push_tmp_variable(temp_rt->lexeme);
			}
			else
			{
				temp_imm = stmt->n2;
				instruction_new(&(lblock->instruction), MIPS_ADD_I,
						temp_rs->lexeme, NULL,
						temp_rd->lexeme, temp_imm, NULL);
				push_tmp_variable(temp_rs->lexeme);
			}
			break;
		case TAC_MINUS:
			prepare_operand(stmt->v2, &temp_rs, 0, 0, currentClass, NULL);
			prepare_operand(stmt->v1, &temp_rd, 0, 0, currentClass, NULL);
			if(stmt->v2 != NULL)
			{
				prepare_operand(stmt->v3, &temp_rt, 0, 0, currentClass, NULL);
				instruction_new(&(lblock->instruction), MIPS_SUBTRACT,
						temp_rs->lexeme, temp_rt->lexeme,
						temp_rd->lexeme, NULL, NULL);
				push_tmp_variable(temp_rs->lexeme);
				push_tmp_variable(temp_rt->lexeme);
			}
			else
			{
				temp_imm = (stmt->n2) * (-1);
				instruction_new(&(lblock->instruction), MIPS_ADD_I,
						temp_rs->lexeme, NULL,
						temp_rd->lexeme, temp_imm, NULL);
				push_tmp_variable(temp_rs->lexeme);
			}
			break;	
		case TAC_AT:
			// First, check and see if there are two operands
			prepare_operand(stmt->v2, &temp_rs, 0, 0, currentClass, NULL);
			if (stmt->v3 == NULL) {
				// There is only one operand in use, so we must be dealing with the address of an object
				struct temp_variables_t *tempResult = pop_tmp_variable(stmt->v1, 1);
				instruction_new(&(lblock->instruction), MIPS_ADD_I, temp_rs->lexeme, NULL, tempResult->name, temp_rs->offset, NULL);
				tempResult->type_name = temp_rs->type_name;
				push_tmp_variable(temp_rs->lexeme);
			} else {
				// There are two operands, so first see if the first operand is a user-defined variable
				if (temp_rs->type == TYPE_CLASS_MEMORY || temp_rs->type == TYPE_FUNCTION_MEMORY) {
					prepare_operand(stmt->v3, &temp_rd, 0, 0, temp_rs->type_name, NULL);
					if (temp_rd->type == TYPE_CLASS_MEMORY || temp_rd->type == TYPE_FUNCTION_MEMORY) {
						// First operand is user defined, so we have to get the address of it first
						struct temp_variables_t *temp = pop_tmp_variable("__mem__", 0);
						instruction_new(&(lblock->instruction), MIPS_LOAD_WORD, temp_rs->lexeme, NULL, temp->name, temp_rs->offset, NULL);
						// The address of the object is in memory, so now add on the offset of operand 3 to get the final address
						struct temp_variables_t *tempResult = pop_tmp_variable(stmt->v1, 1);
						// We are finding the offset of an object in the class
						instruction_new(&(lblock->instruction), MIPS_ADD_I, temp->name, NULL, tempResult->name, temp_rd->offset, NULL);
						tempResult->type_name = temp_rd->type_name;
						tempResult->var_name = temp_rd->var_name;
						push_tmp_variable(temp->name);
						push_tmp_variable(temp_rd->lexeme);
						push_tmp_variable(temp_rs->lexeme);
					} else {
						// We are finding the offset in an array!!!!!! First, load the size of the array type into memory
						// Hold up, partner! Gotta get the size first!
						struct symbol_t *s = symtab_lookup(tolower(stmt->v2), currentClass, NULL, SYMTAB_VARIABLE);
						struct usrdef_t *arr = usrdef_lookup(s->data.type_name);
						int indexSize = compute_symbol_size(arr->td->data.at->td);
						// Sweet, we have the size. Now let's store that in memory!
						struct temp_variables_t *tempIndexSize = pop_tmp_variable("__indexSize__", 0);
						instruction_new(&(lblock->instruction), MIPS_LOAD_IMM, NULL, NULL, tempIndexSize->name, indexSize, NULL);
						// The size of array type is now in memory, so multiply that by the offset to find the actual offset
						struct temp_variables_t *tempPrevIndex = pop_tmp_variable(stmt->v3, 0);
						instruction_new(&(lblock->instruction), MIPS_MULT, tempPrevIndex->name, tempIndexSize->name, NULL, NULL, NULL);
						// Great, the index has been calculated and the result stored in the Lo register. Now let's get it and put it in our own
						// register!
						push_tmp_variable(tempIndexSize->name);
						push_tmp_variable(tempPrevIndex->name);
						struct temp_variables_t *tempIndexResult = pop_tmp_variable("__indexResult__", 0);
						instruction_new(&(lblock->instruction), MIPS_MFLO, NULL, NULL, tempIndexResult->name, NULL, NULL);
						// Ok, so we have it in our temporary registers. Now add that bad boy to the offset of the array itself!
						// Whoa wait! First we have to find the offset of the array
						struct temp_variables_t *tempOffset = pop_tmp_variable("__offset__", 0);
						instruction_new(&(lblock->instruction), MIPS_ADD_I, temp_rs->lexeme, NULL, tempOffset->name, temp_rs->offset, NULL);
						// Great, we have offset in memory. Now let's add the array index and be done!
						struct temp_variables_t *tempFinalResult = pop_tmp_variable(stmt->v1, 1);
						instruction_new(&(lblock->instruction), MIPS_ADD, tempIndexResult->name, tempOffset->name, tempFinalResult->name, NULL, NULL);
						tempFinalResult->type_name = arr->td->data.at->td->name;
						push_tmp_variable(tempOffset->name);	
						push_tmp_variable(tempIndexResult->name);
						push_tmp_variable(temp_rs->lexeme);
					}
				} else {
					// First operand is not user defined, so instead load from whatever variable was used last
					struct temp_variables_t *prevResult = pop_tmp_variable(stmt->v2, 0);
					prepare_operand(stmt->v3, &temp_rd, 0, 0, prevResult->type_name, NULL);
					if (temp_rd->type == TYPE_CLASS_MEMORY) {
						struct temp_variables_t *temp = pop_tmp_variable("__mem__", 0);
						instruction_new(&(lblock->instruction), MIPS_LOAD_WORD, prevResult->name, NULL, temp->name, 0, NULL);
						// Memory location of the object has been loaded, now find the offset of operand 3 to get the final address
						struct temp_variables_t *tempResult = pop_tmp_variable(stmt->v1, 1);
						instruction_new(&(lblock->instruction), MIPS_ADD_I, temp->name, NULL, tempResult->name, temp_rd->offset, NULL);
						tempResult->type_name = temp_rd->type_name;
						push_tmp_variable(prevResult->name);
						push_tmp_variable(temp->name);
					} else {
						// We are finding the offset in an array!!!!!! First, load the size of the array type into memory
						// Hold up, partner! Gotta get the size first!

						struct temp_variables_t *tempPrevOffset = pop_tmp_variable(stmt->v3, 1);
						struct usrdef_t *arr = usrdef_lookup(prevResult->type_name);
						int indexSize = compute_symbol_size(arr->td->data.at->td);
						// Sweet, we have the size. Now let's store that in memory!
						struct temp_variables_t *tempIndexSize = pop_tmp_variable("__indexSize__", 0);
						instruction_new(&(lblock->instruction), MIPS_LOAD_IMM, NULL, NULL, tempIndexSize->name, indexSize, NULL);
						// The size of array type is now in memory, so multiply that by the offset to find the actual offset
						struct temp_variables_t *tempIndex = pop_tmp_variable("__index__", 0);
						instruction_new(&(lblock->instruction), MIPS_MULT, tempPrevOffset->name, tempIndexSize->name, NULL, NULL, NULL);
						// Great, the index has been calculated and the result stored in the Lo register. Now let's get it and put it in our own
						// register!
						push_tmp_variable(tempIndexSize->name);
						push_tmp_variable(tempIndex->name);
						struct temp_variables_t *tempIndexResult = pop_tmp_variable("__indexResult__", 0);
						instruction_new(&(lblock->instruction), MIPS_MFLO, NULL, NULL, tempIndexResult->name, NULL, NULL);
						// Ok, so we have it in our temporary registers. Now add that bad boy to the offset of the array itself!
						struct temp_variables_t *tempFinalResult = pop_tmp_variable(stmt->v1, 1);
						instruction_new(&(lblock->instruction), MIPS_ADD, tempIndexResult->name, prevResult->name, tempFinalResult->name, NULL, NULL);
						tempFinalResult->type_name = arr->td->data.at->td->name;
						push_tmp_variable(tempIndexResult->name);
						push_tmp_variable(tempPrevOffset->name);
					}
				}
			}
			break;
		case TAC_PARAM: {
					// We have the offset of the variable from the TAC, so let's get the address and put it in a temp variable!
					struct temp_variables_t *tempResult = pop_tmp_variable(stmt->v1, 1);
					struct temp_variables_t *prevSP = pop_tmp_variable("__curSP__", 0);
					instruction_new(&(lblock->instruction), MIPS_ADD_I, prevSP->name, NULL, tempResult->name, stmt->n2, NULL);
					break;}
		case TAC_TIMES:
					prepare_operand(stmt->v2, &temp_rs, 0, 0, currentClass, NULL);
					prepare_operand(stmt->v1, &temp_rd, 0, 0, currentClass, NULL);
					if(stmt->v2 != NULL)
					{
						prepare_operand(stmt->v3, &temp_rt, 0, 0, currentClass, NULL);
						instruction_new(&(lblock->instruction), MIPS_MULT,
								temp_rs->lexeme, temp_rt->lexeme,
								NULL, NULL, NULL);
						instruction_new(&(lblock->instruction), MIPS_MFLO,
								NULL, NULL,
								temp_rd->lexeme, NULL, NULL);
						push_tmp_variable(temp_rs->lexeme);
						push_tmp_variable(temp_rt->lexeme);
					}
					else
					{
						// MIPS does not support Divide Immediate
						// need to store the immediate in a help_temp register first
						temp_imm = stmt->n2;
						help_temp = pop_tmp_variable("help_temp", 0);
						instruction_new(&(lblock->instruction), MIPS_ADD_I,
								"$zero", NULL,
								help_temp, temp_imm, NULL);
						instruction_new(&(lblock->instruction), MIPS_MULT,
								temp_rs->lexeme, help_temp,
								NULL, NULL, NULL);
						instruction_new(&(lblock->instruction), MIPS_MFLO,
								NULL, NULL,
								temp_rd->lexeme, NULL, NULL);
						push_tmp_variable(temp_rs->lexeme);
						push_tmp_variable(help_temp);
					}		
					break;
		case TAC_DIV:
					prepare_operand(stmt->v2, &temp_rs, 0, 0, currentClass, NULL);
					prepare_operand(stmt->v1, &temp_rd, 0, 0, currentClass, NULL);
					if(stmt->v2 != NULL)
					{
						prepare_operand(stmt->v3, &temp_rt, 0, 0, currentClass, NULL);
						instruction_new(&(lblock->instruction), MIPS_DIV,
								temp_rs->lexeme, temp_rt->lexeme,
								NULL, NULL, NULL);
						instruction_new(&(lblock->instruction), MIPS_MFLO,
								NULL, NULL,
								temp_rd->lexeme, NULL, NULL);
						push_tmp_variable(temp_rs->lexeme);
						push_tmp_variable(temp_rt->lexeme);
					}
					else
					{
						// MIPS does not support Divide Immediate
						// need to store the immediate in a help_temp register first
						temp_imm = stmt->n2;
						help_temp = pop_tmp_variable("help_temp", 0);
						instruction_new(&(lblock->instruction), MIPS_ADD_I,
								"$zero", NULL,
								help_temp, temp_imm, NULL);
						instruction_new(&(lblock->instruction), MIPS_DIV,
								temp_rs->lexeme, help_temp,
								NULL, NULL, NULL);
						instruction_new(&(lblock->instruction), MIPS_MFLO,
								NULL, NULL,
								temp_rd->lexeme, NULL, NULL);
						push_tmp_variable(temp_rs->lexeme);
						push_tmp_variable(help_temp);
					}		
					break;
		case TAC_MOD: 
					prepare_operand(stmt->v2, &temp_rs, 0, 0, currentClass, NULL);
					prepare_operand(stmt->v1, &temp_rd, 0, 0, currentClass, NULL);
					if(stmt->v2 != NULL)
					{
						prepare_operand(stmt->v3, &temp_rt, 0, 0, currentClass, NULL);
						instruction_new(&(lblock->instruction), MIPS_DIV,
								temp_rs->lexeme, temp_rt->lexeme,
								NULL, NULL, NULL);
						instruction_new(&(lblock->instruction), MIPS_MFLH,
								NULL, NULL,
								temp_rd->lexeme, NULL, NULL);
						push_tmp_variable(temp_rs->lexeme);
						push_tmp_variable(temp_rt->lexeme);
					}
					else
					{
						temp_imm = stmt->n2;
						help_temp = pop_tmp_variable("help_temp", 0);
						instruction_new(&(lblock->instruction), MIPS_ADD_I,
								"$zero", NULL,
								help_temp, temp_imm, NULL);
						instruction_new(&(lblock->instruction), MIPS_DIV,
								temp_rs->lexeme, help_temp,
								NULL, NULL, NULL);
						instruction_new(&(lblock->instruction), MIPS_MFLH,
								NULL, NULL,
								temp_rd->lexeme, NULL, NULL);
						push_tmp_variable(temp_rs->lexeme);
						push_tmp_variable(help_temp);
					}		
					break;
		case TAC_AND:
					prepare_operand(stmt->v2, &temp_rs, 0, 0, currentClass, NULL);
					prepare_operand(stmt->v1, &temp_rd, 0, 0, currentClass, NULL);
					if(stmt->v2 != NULL)
					{
						prepare_operand(stmt->v3, &temp_rt, 0, 0, currentClass, NULL);
						instruction_new(&(lblock->instruction), MIPS_AND,
								temp_rs->lexeme, temp_rt->lexeme,
								temp_rd->lexeme, NULL, NULL);
						push_tmp_variable(temp_rs->lexeme);
						push_tmp_variable(temp_rt->lexeme);
					}
					else
					{
						temp_imm = stmt->n2;
						instruction_new(&(lblock->instruction), MIPS_AND_I,
								temp_rs->lexeme, NULL,
								temp_rd->lexeme, temp_imm, NULL);
						push_tmp_variable(temp_rs->lexeme);
					}		
					break;
		case TAC_OR:
					prepare_operand(stmt->v2, &temp_rs, 0, 0, currentClass, NULL);
					prepare_operand(stmt->v1, &temp_rd, 0, 0, currentClass, NULL);
					if(stmt->v2 != NULL)
					{
						prepare_operand(stmt->v3, &temp_rt, 0, 0, currentClass, NULL);
						instruction_new(&(lblock->instruction), MIPS_OR,
								temp_rs->lexeme, temp_rt->lexeme,
								temp_rd->lexeme, NULL, NULL);
						push_tmp_variable(temp_rs->lexeme);
						push_tmp_variable(temp_rt->lexeme);
					}
					else
					{
						temp_imm = stmt->n2;
						instruction_new(&(lblock->instruction), MIPS_OR_I,
								temp_rs->lexeme, NULL,
								temp_rd->lexeme, temp_imm, NULL);
						push_tmp_variable(temp_rs->lexeme);
					}		
					break;
		case TAC_NEGATE:{
					prepare_operand(stmt->v2, &temp_rs, 0, 0, currentClass, NULL);
					prepare_operand(stmt->v1, &temp_rd, 0, 0, currentClass, NULL);
					struct temp_variables_t *negative_one = pop_tmp_variable("negative_one", 0);
					instruction_new(&(lblock->instruction), MIPS_ADD_I,
							"$zero", NULL,
							negative_one->name, -1, NULL);
					instruction_new(&(lblock->instruction), MIPS_XOR,
							temp_rs->lexeme, negative_one->name,
							temp_rd->lexeme, NULL, NULL);
					instruction_new(&(lblock->instruction), MIPS_ADD_I,
							temp_rd->lexeme, NULL,
							temp_rd->lexeme, 1, NULL);
					push_tmp_variable(temp_rs->lexeme);
					push_tmp_variable(negative_one->name);
					break;}
		case TAC_NOT:{
				     struct temp_variables_t *tempPrevResult = pop_tmp_variable(stmt->v2, 0);
				     struct temp_variables_t *tempReg = pop_tmp_variable("__temp__", 0);
				     prepare_operand(stmt->v1, &temp_rd, 0, 0, currentClass, NULL);
				     instruction_new(&(lblock->instruction), MIPS_ADD_I,
						     "$zero", NULL,
						     tempReg->name, -1, NULL);
				     instruction_new(&(lblock->instruction), MIPS_XOR,
						     tempReg->name, tempPrevResult->name,
						     temp_rd->lexeme, NULL, NULL);	
				     push_tmp_variable(tempPrevResult->name);
				     push_tmp_variable(tempReg->name);
				     break;}
		case TAC_NEW: {
				      // First, get the class that's being created and put it on the stack
				      struct usrdef_t *class = usrdef_lookup(stmt->v2);
				      // Get a temporary register and store the current value of the stack pointer in it for later use
				      struct temp_variables_t *tempPrevSP = pop_tmp_variable("__sp__", 0);
				      instruction_new(&(lblock->instruction), MIPS_ADD_I, MIPS_SP, NULL, tempPrevSP->name, 0, NULL);
				      // Now, increment the stack pointer by the size of the class
				      instruction_new(&(lblock->instruction), MIPS_ADD_I, MIPS_SP, NULL, MIPS_SP, (int)class->size, NULL);
				      // Awesome, memory is reserved! Now let's see what kind of register is going to point to this memory
				      prepare_operand(stmt->v1, &temp_rd, 0, 0, currentClass, NULL);
				      if (temp_rd->type == TYPE_FUNCTION_MEMORY || temp_rd->type == TYPE_CLASS_MEMORY) {
					      // This is a variable in the current class, so store the pointer to the old sp in its memory space
					      // First, get the offset of the variable in the current class
					      struct temp_variables_t *tempOffset = pop_tmp_variable("__offset__", 0);
					      instruction_new(&(lblock->instruction), MIPS_ADD_I, MIPS_GLOBAL_PARENT_S0, NULL, tempOffset->name, temp_rd->offset, NULL);
					      // Great, the location is now in the register. Now store the old stack pointer in there and be done!
					      instruction_new(&(lblock->instruction), MIPS_STORE_WORD, tempPrevSP->name, NULL, tempOffset->name, 0, NULL);
					      push_tmp_variable(tempOffset->name);
				      } else {
					      // The register is pointing to a memory location already, so just store it there!
					      struct temp_variables_t *tempPrevResult = pop_tmp_variable(stmt->v1, 0);
					      instruction_new(&(lblock->instruction), MIPS_STORE_WORD, tempPrevSP->name, NULL, tempPrevResult->name, 0, NULL);
					      push_tmp_variable(tempPrevResult->name);
				      }
				      push_tmp_variable(tempPrevSP->name);
				      break;}
		default: break;
	}

	return;
}

/* ----------------------------------------------------------------------- 
 * Appends a MIPS instruction to the lir
 * ----------------------------------------------------------------------- 
 */
void instruction_new(struct mc_t **ss, int opcode, char *rs, 
		char *rt, char *rd, int imm, char *label) 
{
	struct mc_t *instruction;
	if(*ss == NULL) /* Is the list of statements empty? */
	{
		*ss = malloc(sizeof(struct mc_t)); /* Put the statement in
						      the list */
		instruction = *ss;
	}
	else 
	{
		instruction = *ss;
		while (instruction->next != NULL) 
			instruction = instruction->next; /* Find the end of the list */
		instruction->next = malloc(sizeof(struct mc_t)); /* Append the statement */
		instruction = instruction->next;
	}
	instruction->opcode = opcode;
	instruction->rs = rs;
	instruction->rt = rt;
	instruction->rd = rd;
	instruction->imm = imm;
	instruction->label = label;
}
